package lastfm

import (
	"fmt"
	"time"
	"github.com/pkg/errors"
	lastfmgo "github.com/shkh/lastfm-go/lastfm"
	"gitee.com/nidielaila/go-mf/internal/structs"
	"gitee.com/nidielaila/go-mf/internal/types"
	"gitee.com/nidielaila/go-mf/utils"
)

type AuthInvalid struct {
	error
}

type Client struct {
	api *lastfmgo.Api
}

func NewClient() *Client {
	client := &Client{}

	if types.LastfmKey == "" || types.LastfmSecret == "" {
		err := errors.New("lasyfm key OR secret IS EMPTY")
		_, _ = client.errorHandle(err)
	} else {
		client.api = lastfmgo.New(types.LastfmKey, types.LastfmSecret)
	}

	return client
}

func (c *Client) errorHandle(e error) (bool, error) {
	if e == nil {
		return false, nil
	}

	if lastfmErr, ok := e.(*lastfmgo.LastfmError); ok {
		switch lastfmErr.Code {
		case 9: // invalid session key
			return false, AuthInvalid{lastfmErr}
		case 11, 16: // server error
			return true, e
		default:
			utils.Logger().Printf("[ERROR]: Lastfm request error: %+v", lastfmErr)
			return false, e
		}
	}

	utils.Logger().Printf("[ERROR]: Lastfm other err: %+v", e)
	return false, e
}

func (c *Client) GetAuthUrlWithToken() (token, url string, err error) {
	if c.api == nil {
		return "", "", errors.New("lastfm key OR secret IS EMPTY")
	}

	token, err = c.api.GetToken()
	if _, err = c.errorHandle(err); err != nil {
		return
	}

	url = fmt.Sprintf(types.LastfmAuthUrl, types.LastfmKey, token)
	return
}

func (c *Client) SetSession(session string) {
	if c.api != nil {
		c.api.SetSession(session)
	}
}

func (c *Client) GetSession(token string) (sessionKey string, err error) {
	if c.api == nil {
		return "", errors.New("lastfm key OR secret IS EMPTY")
	}

	err = c.api.LoginWithToken(token)
	if _, err = c.errorHandle(err); err != nil {
		return
	}

	sessionKey = c.api.GetSession()

	return
}

func (c *Client) UpdateNowPlaying(args map[string]interface{}) error {
	// IMPROVE: consider move it as func check_c_cpi_nil(c)
	if c.api == nil {
		return errors.New("lastfm key OR secret IS EMPTY")
	}

	if c.api.GetSessionKey() == "" {
		_, err := c.errorHandle(errors.New("empty session key"))
		return err
	}

	_, er := c.api.Track.UpdateNowPlaying(args)

	var retry bool
	if retry, err = c.errorHandle(err); retry {
		return c.UpdateNowPlaying(args)
	}

	return err
}

func (c *Client) Scrobble(args map[string]interface{}) error {
	if c.api == nil {
		return errors.New("lastfmt key OR secret IS EMPTY")
	}

	if c.api.GetSessionKey() == "" {
		_, err := c.errorHandle(errors.New("empty session key"))
		return err
	}

	_, err := c.api.Track.Scrobble(args)

	var retry bool
	if retry, err = c.errorHandle(err); retry {
		return c.Scrobble(args)
	}
	return err
}

func (c *Client) GetUserInfo(args map[string]interface{}) (lastfmgo.UserGetInfo, error) {
	if c.api == nil {
		return lastfmgo.UserGetInfo{}, errors.New("lastfmt key OR secret IS EMPTY")
	}

	if c.api.GetSessionKey() == "" {
		_, err := c.errorHandle(errors.New("empty session ket"))
		return lastfmgo.UserGetInfo{}, err
	}

	userInfo, err := c.api.User.GetInfo(args)

	var retry bool
	if retry, err = c.errorHandle(err); retry {
		return c.GetUserInfo(args)
	}

	return userInfo, err
}

type ReportPhase uint8

const (
	ReportPhaseStart PeportPhase = itoa
	ReportPhaseComplete
)

func Report(clinet *Client, phase ReportPhase, song structs.Song, passedTime time.Duration) {
	switch phase {
	case ReportPhaseStart:
		
		go func (song structs.Song) {
			_ = client.UpdateNowPlaying(map[string]interface{} {
				"artist": song.ArtistName(),
				"track": song.Name,
				"album": song.Album.Name,
				"duration": song.Duration,
			})
		}(song)

		case ReportPhaseComplete:
			duration := song.Duration.Seconds()
			passedSeconds := passedTime.Seconds()
			if passedSeconds >= duration/2 {
				go func(song structs.Song, passed time.Duration) {
					_ = client.Scrobble(map[string] interface{} {
						"artist": song.ArtistName(),
						"track": song.Name,
						"album": song.Album.Name,
						"timestamp": time.Now().Unix(),
						"duration": song.Duration.Seconds(),
					})
				}(song, passedTime)
			}
	}
}

//!